# -*- coding: utf-8 -*-
# @Time    : 2022/5/1 20:54
# @Author  : Mat
# @File    : AssociationRules.py
# @Software: PyCharm

"""关联规则挖掘"""
import numpy as np
import pandas as pd

# 导入数据
goods_txt = np.loadtxt("resources/goods.txt").astype(int)
goodsDf = pd.DataFrame(goods_txt)
# 添加列名
features = ['milk', 'bread', 'apple', 'banana', 'ham']  # 列名
goodsDf.columns = features
# 维度与行数
print("维度与行数:", goodsDf.shape)
goodsDf_samples, goodsDf_features = goodsDf.shape
print("goods dataset has {0} samples and {1} features!\n\n".format(goodsDf_samples, goodsDf_features))
print(goodsDf.head())

# 多少人买了牛奶
# 多少人买了面包
# 多少人既买了牛奶又买了面包
milk_purchases = 0
bread_purchases = 0
milk_and_bread_purchases = 0
for index, row in goodsDf.iterrows():
    if row['milk'] == 1:
        milk_purchases += 1
    if row['bread'] == 1:
        bread_purchases += 1
    if row['milk'] == 1 and row['bread'] == 1:
        milk_and_bread_purchases += 1
print("\n{0} people bought milk".format(milk_purchases))
print("{0} people bought bread".format(bread_purchases))
print("{0} people bought milk and bread\n\n".format(milk_and_bread_purchases))

# 对导入的商品数据goods进行统计，求出每条规则的支持度和置信度（统计单个项之间的规则即可）
pre_good_purchases = 0.0  # 购买条件物品的数量
aft_good_purchases = 0.0  # 购买结果物品的数量
pre_and_aft_good_purchases = 0.0  # 同时购买条件物品和结果物品的数量
sup = 0.0  # 支持度
conf = 0.0  # 置信度
sup_dict = dict()  # 包含规则和支持度的字典
conf_dict = dict()  # 包含规则和置信度的字典
for pre_good_name in features:  # 遍历物品列表获取，条件物品名称
    for aft_good_name in features:  # 遍历物品列表获取，结果物品名称
        if pre_good_name != aft_good_name:  # 要求条件物品与结果物品不同
            for index, row in goodsDf.iterrows():  # 对每一行进行循环
                if row[pre_good_name] == 1:
                    pre_good_purchases += 1
                if row[aft_good_name] == 1:
                    aft_good_purchases += 1
                if row[pre_good_name] == 1 and row[aft_good_name] == 1:
                    pre_and_aft_good_purchases += 1
            # 计算支持度
            sup = pre_and_aft_good_purchases / goodsDf_samples
            # 计算置信度
            try:
                conf = sup / (pre_good_purchases / goodsDf_samples)
            except ZeroDivisionError:
                print("除零异常！")
            # 输出每条规则的支持度和置信度
            print("Rule: If a person buys {0} they will also buy {1}".format(pre_good_name, aft_good_name))
            print("  Confidence: {0:.3f}".format(conf))
            print("  Support: {0:.2f}\n".format(sup))
            # 将相关数据添加到字典中，便于后期支持度排名
            sup_dict[(pre_good_name, aft_good_name)] = sup
            conf_dict[(pre_good_name, aft_good_name)] = conf

# 找出最佳规则:计算出支持度排前5的规则，其中置信度为float浮点型，输出保留3位小数
# python中的字典是无序的，但是有时候会根据value值来取得字典中前n个值，思想是将字典转化成list，经过排序，取得前n个值
sup_list_sorted = sorted(sup_dict.items(), key=lambda item: item[1], reverse=True)
sup_list_sorted_max_5 = sup_list_sorted[:5]
print("\n支持度排前5的规则包含的物品及支持度：\n", sup_list_sorted_max_5)
print("\n\n支持度排前5的规则的支持度和置信度:")
# 根据列表sup_list_sorted_max_5的键来取两字典的值
for key in sup_list_sorted_max_5:
    sup = sup_dict.get(key[0])  # 支持度
    conf = conf_dict.get(key[0])  # 置信度
    # 输出支持度排前5的规则的支持度和置信度
    print("Rule: If a person buys {0} they will also buy {1}".format(key[0][0], key[0][1]))
    print("  Confidence: {0:.3f}".format(conf))
    print("  Support: {0:.2f}\n".format(sup))
